
const Service = require('egg').Service;
const md5 = require('md5');

const { BLOCK_ARTICLE, BLOCK_ARTICLE_FREE, ARTICLE_PAYMENT_ID, ARTICLE_UNLOCK, USER_DATA, EXE_USER_TOKEN } = require('../constant/redis');

class BlockService extends Service {

    _cutBlocks(blocks) {
        let free = [];
        let pay = [];
        let lock;
        const lockIndex = blocks.findIndex(item => item.type === 'lock');
        if (lockIndex === -1) {
            free = blocks
        } else {
            lock = blocks[lockIndex]
            free = blocks.splice(0, lockIndex + 1);
            pay = blocks
        }
        return { free, lock, pay }
    }

    // 将块文章封装成想要的格式
    async _wrapBlocks(blocks) {
        const ctx = this.ctx;
        const count = await ctx.service.cache.scard([ARTICLE_UNLOCK, blocks._id]);
        blocks.unlockCount = count
        return blocks
    }

    async _createPay(data, articleId) {
        const ctx = this.ctx;
        const blocks = JSON.parse(data.blocks).blocks;
        const result = this._cutBlocks(blocks);
        if (result.lock && result.lock.type === 'lock' && result.lock.data && result.lock.data.type && result.lock.data.amount) {
            let paymentId;
            if (articleId) {
                paymentId = await ctx.service.cache.get([ARTICLE_PAYMENT_ID, articleId]);
            }
            const payData = {
                type: result.lock.data.type,
                amount: parseFloat(result.lock.data.amount),
                creator: ctx.state.user.id
            }
            if (paymentId) {
                const result = await this.ctx.model.Payment.findOneAndUpdate({ creator: ctx.state.user.id, _id: paymentId }, payData).lean();
                if (!result) {
                    await ctx.service.cache.set([ARTICLE_PAYMENT_ID, articleId], null); // 如果修改支付失败，清空缓存里面的支付方式
                    throw new Error('支付方式保存失败，请重试！');
                }
            } else {
                const pay = await ctx.model.Payment(
                    payData
                ).save();
                paymentId = pay._id
                await ctx.service.cache.set([ARTICLE_PAYMENT_ID, articleId], paymentId);
            }
            return paymentId
        }
    }

    async getBlocks(params = {}) {
        const ctx = this.ctx;
        const pageSize = params && parseInt(params.pageSize) || 10; // 使用分页
        const page = params && parseInt(params.page) || 1;
        delete params.pageSize;
        delete params.page;
        if (ctx.state.user) {
            params.author = ctx.state.user.id;
        }

        const list = await ctx.model.Block.find(params, ' -blocks').limit(pageSize).skip(pageSize * (page - 1)).sort('-createDate').lean();
        const count = await ctx.model.Block.find(params).count().lean();
        return {
            list,
            count
        }
    }

    async createBlock(data) {
        const ctx = this.ctx;
        const payId = await this._createPay(data);
        if (payId) {
            data.payment = payId;
        }
        return ctx.model.Block({ ...data, author: ctx.state.user.id }).save()
    }

    async deleteBlock(id) {
        const author = this.ctx.state.user.id;
        await this.delRedis(id);
        return this.ctx.model.Block.findOneAndRemove({ author, _id: id }).lean()
    }

    async editBlock(id) {
        const author = this.ctx.state.user.id;
        return this.ctx.model.Block.findOne({ author, _id: id }).lean()
    }

    async updateBlock(id, data) {
        const ctx = this.ctx;
        const author = ctx.state.user.id;
        const payId = await this._createPay(data, id);
        if (payId) {
            data.payment = payId;
        }
        const result = await ctx.model.Block.findOneAndUpdate({ author, _id: id }, data, { new: true }).lean(); // {new: true} 返回修改之后的result
        if (data.blocks && result.status === 2) {
            // const blocks = JSON.parse(data.blocks);
            await this.setBlocksToRedis(id, result);
        }
        return result
    }

    async publishBlock(id) {
        const ctx = this.ctx;
        const author = ctx.state.user.id;
        const result = await ctx.model.Block.findOneAndUpdate({ author, _id: id }, {
            $set: { status: 2, publishDate: new Date() }
        }).lean();
        // const blocks = JSON.parse(result.blocks);
        await this.setBlocksToRedis(id, result);
        return result
    }

    /**
     * 获取一篇文章可以免费观看的部分，如果缓存里面没有，则存入缓存
     *
     * @param {*} id
     * @return {*} 
     * @memberof BlockService
     */
    async showFreeBlock(id) {
        const ctx = this.ctx;
        let free = await ctx.service.cache.get([BLOCK_ARTICLE_FREE, id]);
        if (!free) {
            const result = await ctx.model.Block.findOne({ _id: id, status: 2 }).lean();
            await this.setBlocksToRedis(id, result);
            return await ctx.service.cache.get([BLOCK_ARTICLE_FREE, id]);
        }
        return await this._wrapBlocks(free);
    }

    // 查看完整文章详情
    async showBlock(id) {
        const ctx = this.ctx;
        const userId = ctx.state.user.id;
        let unLocked = await ctx.service.cache.sismember([ARTICLE_UNLOCK, id], userId);
        if (!unLocked) {
            return this.showFreeBlock(id);
        }
        let all = await ctx.service.cache.get([BLOCK_ARTICLE, id]);
        if (!all) {
            all = await ctx.model.Block.findOne({ _id: id, status: 2 }).lean();
            await this.setBlocksToRedis(id, all);
        }
        return await this._wrapBlocks(all);
    }

    /**
     * 将块文章的内容分为免费部分和完整两部分存储到Redis，并且返回免费的内容，缓存8H
     *
     * @param {*} id
     * @param {*} blockDoc
     * @return {*} 
     * @memberof BlockService
     */
    async setBlocksToRedis(id, blockDoc) {
        const ctx = this.ctx;
        let blocks = JSON.parse(blockDoc.blocks);
        let free = [];
        for (let item of blocks.blocks) {
            free.push(item)
            if (item.type === 'lock') {
                break;
            }
        }
        await ctx.service.cache.set([BLOCK_ARTICLE, id], blockDoc, ctx.app.constant.TOKEN_EX * 60 * 60);
        blocks.blocks = free;
        blockDoc.blocks = JSON.stringify(blocks);
        await ctx.service.cache.set([BLOCK_ARTICLE_FREE, id], blockDoc, ctx.app.constant.TOKEN_EX * 60 * 60);
        return blockDoc
    }

    async delRedis(id) {
        const ctx = this.ctx;
        await ctx.service.cache.del([BLOCK_ARTICLE, id]);
        await ctx.service.cache.del([BLOCK_ARTICLE_FREE, id]);
    }

    /**
     * 解锁文章
     *
     * @param {*} id
     * @returns
     * @memberof BlockService
     */
    async unlockBlock(id) {
        const ctx = this.ctx;
        const userId = ctx.state.user.id;
        let unLocked = await ctx.service.cache.sismember([ARTICLE_UNLOCK, id], userId);
        if (!unLocked) {
            // 开始处理解锁文章
            let free = await ctx.service.cache.get([BLOCK_ARTICLE_FREE, id]);
            if (free && free.payment && free.payment.type === 1) {
                const userData = await ctx.service.cache.get([USER_DATA, userId]);
                const sendId = userData.openId.split('_')[2];
                const receiveId = free.payment.creator.openId.split('_')[2];
                console.log((sendId + '赠送' + receiveId));
                await this.sendPresent(sendId, receiveId, 1, free.payment.amount)
            }
            await ctx.service.cache.sadd([ARTICLE_UNLOCK, id], userId);
        }
        return await ctx.service.cache.get([BLOCK_ARTICLE, id]);
    }

    /**
     * 发送礼物给指定的人
     *
     * @param {*} sendId
     * @param {*} receiveId
     * @param {*} presentId
     * @param {*} count
     * @returns
     * @memberof BlockService
     */
    async sendPresent(sendId, receiveId, presentId, count) {
        const ctx = this.ctx;
        const url = ctx.app.constant.EXE.API_URL;
        // 加签
        const setSignHeaders = () => {
            const nonce = Math.random().toString().slice(2, 10);
            const timeStamp = new Date().getTime();
            const signValue = [sendId, timeStamp, nonce].join('&');
            const md5SignValue = md5(signValue);
            const md5SignValueLen = md5SignValue.length;
            const md5SignArr = md5SignValue.split('');
            const firstPart = md5SignArr.slice(0, 6).reverse();
            const lastPart = md5SignArr.slice(md5SignValueLen - 8, md5SignValueLen).reverse();
            const midPart = lastPart.concat(firstPart).join('');
            const signature = md5(md5(midPart)).toUpperCase();
            return { timeStamp, nonce, signature }
        }

        let token = await ctx.service.cache.get([EXE_USER_TOKEN, sendId]);

        const result = await ctx.curl(`${url}/api/Present`, {
            dataType: 'json',
            timeout: 5000,
            method: 'POST',
            headers: {
                "Accept-Language": 'zh-CN',
                token,
                ...setSignHeaders()
            },
            data: JSON.stringify({
                "count": count,
                "id": presentId,
                "to_user_id": receiveId
            }),
            contentType: "application/json"
        });

        if (result && result.data) {
            if (result.data.success) {
                return result.data
            } else {
                throw new Error(result.data.Message || result.data.msg || result.data.data)
            }
        } else {
            throw new Error('送礼失败！')
        }
    }
}

module.exports = BlockService;
